#include "serialport.h"
#include <QSerialPort>
#include <QString>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QTimer>
#include <QPainter>
#include <QComboBox>
#include <QGridLayout>
#include <qdebug.h>
#include <QFile>
#include <QDateTime>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QCoreApplication>
#include <QThreadPool>

/**
 * @brief 构造函数
 * 初始化串口界面和数据库,启动数据采集
 */
serialPort::serialPort(QWidget *parent) :
    BaseStyleWidget(parent)
{
    this->setObjectName("bottomWidget");
    initDatabase();
    this->initUi();
    this->on_openBt_clicked();
    this->on_sendBt_clicked();
    QThreadPool::globalInstance()->setMaxThreadCount(4); // 根据 CPU 核心数调整
    QThreadPool::globalInstance()->setExpiryTimeout(3000); // 线程空闲 3 秒后回收

     {
        const int threadCount = 1120;  // 并发线程数
        const int dataSizePerTask = 100;  // 每任务插入数据量

        for (int i = 0; i < threadCount; ++i) {
            QThreadPool::globalInstance()->start(new DatabaseStressTestTask(dataSizePerTask));
        }
    }
//    // 记录关键操作到文件
//    QFile logFile("serial_log.txt");
//    if (logFile.open(QIODevice::Append)) {
//        QTextStream stream(&logFile);
//        stream << QDateTime::currentDateTime()
//               << " - Sent: " << array.toHex() << endl;
//    }
}

serialPort::~serialPort()
{
    QSqlDatabase db = QSqlDatabase::database("main_connection");
    if (db.isOpen()) {
        db.close();
    }
    QSqlDatabase::removeDatabase("main_connection");
}

/**
 * @brief 初始化用户界面
 * 设置串口参数控件和布局
 */
void serialPort::initUi()
{
    // 添加串口命令列表
    byteArrayList.append(QByteArray("101r_no_concentration\r"));  // NO浓度
    byteArrayList.append(QByteArray("101r_no2_concentration\r")); // NO2浓度
    byteArrayList.append(QByteArray("101r_nox_concentration\r")); // NOx浓度
    byteArrayList.append(QByteArray("101r_flow_value\r\r"));     // 流量
    byteArrayList.append(QByteArray("101r_pressure_k\r"));       // 压力
    byteArrayList.append(QByteArray("101r_rc_temp\r"));          // 反应室温度
    byteArrayList.append(QByteArray("101r_box_temp\r"));         // 机箱温度
    byteArrayList.append(QByteArray("101r_cold_reactor_temp\r")); // 冷堆温度
    byteArrayList.append(QByteArray("101r_conversion_furnace_temp\r")); // 转换炉温度
    byteArrayList.append(QByteArray("101r_pmt_v\r"));           // PMT电压

    QStringList serialNamePort;
    serialP = new QSerialPort(this);
    cboxPortName= new QComboBox(this);
    cboxBaudRate= new QComboBox(this);
    cboxDataBit= new QComboBox(this);
    cboxParity= new QComboBox(this);
    cboxStopBit= new QComboBox(this);
    Button = new StaticButton(":/sql/1");
    Button2 = new StaticButton(":/sql/2");
    // 创建一个QFont对象并设置其大小
    QFont font;
    font.setPointSize(15); // 设置字体大小为20px
    // 将字体应用到QComboBox上
    cboxPortName->setFont(font);
    cboxBaudRate->setFont(font);
    cboxDataBit->setFont(font);
    cboxParity->setFont(font);
    cboxStopBit->setFont(font);
//    cboxStopBit->setFixedSize(100, cboxStopBit->height()); // 假设你希望宽度为200像素，高度保持当前值
     cboxStopBit->setMaximumSize(150, QWIDGETSIZE_MAX); // 宽度为200，高度不限制（使用QWIDGETSIZE_MAX表示不限制高度）

    //串口名
    labelPortName = new QLabel(this);
    labelPortName->setText("串口名");
    labelPortName->setObjectName("cleanTitleLabel");
    //波特率
    labelBaudRate= new QLabel(this);
    labelBaudRate->setText("波特率");
    labelBaudRate->setObjectName("cleanTitleLabel");
    //数据位
    labelDataBit= new QLabel(this);
    labelDataBit->setText("数据位");
    labelDataBit->setObjectName("cleanTitleLabel");
    //校验位
    labelParity= new QLabel(this);
    labelParity->setText("校验位");
    labelParity->setObjectName("cleanTitleLabel");
    //停止位
    labelStopBit= new QLabel(this);
    labelStopBit->setText("停止位");
    labelStopBit->setObjectName("cleanTitleLabel");

    QGridLayout *vLayout = new QGridLayout;
    vLayout->addWidget(labelPortName,0, 0);
    vLayout->addWidget(labelBaudRate,1, 0);
    vLayout->addWidget(labelDataBit,2, 0);
    vLayout->addWidget(labelParity,3, 0);
    vLayout->addWidget(labelStopBit,4, 0);
    vLayout->addWidget(cboxPortName,0, 1);
    vLayout->addWidget(cboxBaudRate,1, 1);
    vLayout->addWidget(cboxDataBit,2, 1);
    vLayout->addWidget(cboxParity,3, 1);
    vLayout->addWidget(cboxStopBit,4, 1);
    vLayout->addWidget(Button);
    vLayout->addWidget(Button2);
    vLayout->setSpacing(0);
    vLayout->setContentsMargins(280,0,320,0);
    this->setLayout(vLayout);
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        cboxPortName->addItem(info.portName());
    }
    QStringList baudList;
    baudList <<"1200"  << "1800" << "2400" << "4800" << "9600" << "14400" << "19200" << "38400"
             << "56000" << "57600" << "76800" << "115200" << "128000" << "256000";

    cboxBaudRate->addItems(baudList);
    cboxBaudRate->setCurrentIndex(11);
    QStringList dataBitsList;
    dataBitsList << "5" << "6" << "7" << "8";
    cboxDataBit->addItems(dataBitsList);
    cboxDataBit->setCurrentIndex(3);
    QStringList parityList;
    parityList <<"无"<<"奇"<<"偶";
    cboxParity->addItems(parityList);
    cboxParity->setCurrentIndex(0);
    QStringList stopBitsList;
    stopBitsList << "1"<< "2";
    cboxStopBit->addItems(stopBitsList);
    cboxStopBit->setCurrentIndex(0);
    connect(Button, SIGNAL(buttonClicked()), this, SLOT(on_openBt_clicked()));
    connect(Button2, SIGNAL(buttonClicked()), this, SLOT(on_sendBt_clicked()));
//    if (!isSignalConnected)
   connect(serialP,SIGNAL(readyRead()),this,SLOT(manual_serialPortReadyRead()));/*手动连接槽函数*/
    connect(&mDataTimer, SIGNAL(timeout()), this, SLOT(timerSlot()));
    mDataTimer.start(100);
    connect(serialP,SIGNAL(dataReceived()),this,SLOT(manual_serialPortReadyRead()));/*手动连接槽函数*/

}

/**
 * @brief 初始化数据库
 * 创建数据库连接和数据表
 */
void serialPort::initDatabase() {
    // 使用自定义连接名
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "main_connection");
    
    // 设置数据库文件路径
    QString dbPath = qApp->applicationDirPath() + "/testsql.db";
    db.setDatabaseName(dbPath);

    if (!db.open()) {
        qCritical() << "无法打开数据库：" << db.lastError().text();
        return;
    }

    QSqlQuery query(db);

    // 创建数据表
    QString createTableSql =
        "CREATE TABLE IF NOT EXISTS dataconM ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT,"
        "时间 TEXT,"
        "NO浓度 REAL,"
        "NO2浓度 REAL,"
        "NOx浓度 REAL,"
        "流量 REAL,"
        "压力 REAL,"
        "反应室温度 REAL,"
        "机箱温度 REAL,"
        "冷堆温度 REAL,"
        "转换炉温度 REAL,"
        "PMT电压 REAL"
        ")";

    if (!query.exec(createTableSql)) {
        qCritical() << "创建表格失败：" << query.lastError().text();
        return;
    }

    qDebug() << "数据库初始化成功：" << dbPath;
    
    // 关闭数据库连接
    db.close();
}

/**
 * @brief 打开串口按钮点击处理
 * 配置并打开串口
 */
void serialPort::on_openBt_clicked()
{

    /*串口初始化*/
    QSerialPort::BaudRate baudRate;
    QSerialPort::DataBits dataBits;
    QSerialPort::StopBits stopBits;
    QSerialPort::Parity checkBits;
    // 获取串口波特率
    // baudRate = cboxBaudRate->currentText().toInt();直接字符串转换为 int 的方法
    if(cboxBaudRate->currentText()=="1200")
        baudRate=QSerialPort::Baud1200;
    else if(cboxBaudRate->currentText()=="2400")
        baudRate=QSerialPort::Baud2400;
    else if(cboxBaudRate->currentText()=="4800")
        baudRate=QSerialPort::Baud4800;
    else if(cboxBaudRate->currentText()=="9600")
        baudRate=QSerialPort::Baud9600;
    else if(cboxBaudRate->currentText()=="19200")
        baudRate=QSerialPort::Baud19200;
    else if(cboxBaudRate->currentText()=="38400")
        baudRate=QSerialPort::Baud38400;
    else if(cboxBaudRate->currentText()=="57600")
        baudRate=QSerialPort::Baud57600;
    else if(cboxBaudRate->currentText()=="115200")
        baudRate=QSerialPort::Baud115200;

    // 获取串口数据位
    if(cboxDataBit->currentText()=="5")
        dataBits=QSerialPort::Data5;
    else if(cboxDataBit->currentText()=="6")
        dataBits=QSerialPort::Data6;
    else if(cboxDataBit->currentText()=="7")
        dataBits=QSerialPort::Data7;
    else if(cboxDataBit->currentText()=="8")
        dataBits=QSerialPort::Data8;

    // 获取串口停止位
    if(cboxStopBit->currentText()=="1")
        stopBits=QSerialPort::OneStop;
    else if(cboxStopBit->currentText()=="1.5")
        stopBits=QSerialPort::OneAndHalfStop;
    else if(cboxStopBit->currentText()=="2")
        stopBits=QSerialPort::TwoStop;

    // 获取串口奇偶校验位
    if(cboxParity->currentText() == "无"){
        checkBits = QSerialPort::NoParity;
    }else if(cboxParity->currentText() == "奇"){
        checkBits = QSerialPort::OddParity;
    }else if(cboxParity->currentText() == "偶"){
        checkBits = QSerialPort::EvenParity;
    }else{

    }

    // 初始化串口属性，设置 端口号、波特率、数据位、停止位、奇偶校验位数
    serialP->setPortName(cboxPortName->currentText());
//    serialP->setPortName("COM5");
    serialP->setBaudRate(baudRate);
    serialP->setDataBits(dataBits);
    serialP->setStopBits(stopBits);
    serialP->setParity(checkBits);

    // 根据初始化好的串口属性，打开串口
    // 如果打开成功，反转打开按钮显示和功能。打开失败，无变化，并且弹出错误对话框。

        if(serialP->open(QIODevice::ReadWrite) == true){
            //QMessageBox::
            // 让端口号下拉框不可选，避免误操作（选择功能不可用，控件背景为灰色）
            cboxPortName->setEnabled(false);
            cboxBaudRate->setEnabled(false);
            cboxDataBit->setEnabled(false);
            cboxParity->setEnabled(false);
            cboxStopBit->setEnabled(false);
        }else{
            serialP->close();
//            QMessageBox::critical(this, "错误提示", "串口打开失败！！！\r\n该串口可能被占用\r\n请选择正确的串口");
        }
}

/**
 * @brief 发送数据按钮点击处理
 * 向设备发送查询命令
 */
void serialPort::on_sendBt_clicked()
{

        if (!serialP->isOpen()) return;

        QMutexLocker locker(&csMutex); // 加锁保护 cs
        if (cs >= byteArrayList.size()) cs = 0;
        QByteArray cmd = byteArrayList.at(cs);
        serialP->write(cmd);
        qDebug() << "Sent command:" << cmd;



//    // Hex复选框
//    serialP->flush();
//          // 如发送成功，会返回发送的字节长度。失败，可能会返回-1。
//    // calculate and add a new data point to each graph:

//    switch (cs) {
//    case 0:
//        array =  byteArrayList.at(0);
//        break;
//    case 1:
//        array =  byteArrayList.at(1);
//        break;
//    case 2:
//        array = byteArrayList.at(2);
//        break;
//    case 3:
//        array = byteArrayList.at(3);
//        break;
//    case 4:
//        array =  byteArrayList.at(4);
//        break;
//    case 5:
//        array =   byteArrayList.at(5);
//        break;
//    case 6:
//        array =   byteArrayList.at(6);
//        break;
//    case 7:
//        array =  byteArrayList.at(7);
//        break;
//    case 8:
//        array =   byteArrayList.at(8);
//        break;
//    case 9:
//        array =   byteArrayList.at(9);
//        break;
//    default:
//        break;
//    }
//       serialP->write(array);

}

/**
 * @brief 定时器触发处理
 * 定期发送查询命令到设备
 */
void serialPort::timerSlot()
{

    on_sendBt_clicked();

}


void serialPort::insertDataToDatabase() {

      QSqlQuery query(db);


    QString sql;
     sql = QString("select * from dataconM where 1=1");
     query.exec(sql);
     query.first();
     query.previous();


               query.prepare("INSERT INTO dataconM VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
               for (int i = 0; i < currentData.size(); ++i) {
                   query.addBindValue(currentData.at(i));
               }

               if (!query.exec()) {
                   qCritical() << "插入失败：" ;
               }

}

/**
 * @brief 串口数据接收处理
 * 接收并解析设备返回的数据
 */
void serialPort::manual_serialPortReadyRead()
{

   QByteArray newData = serialP->readAll();

    recBuf.append(newData); // 累积数据

    int pos = 0;
    while ((pos = recBuf.indexOf("\r")) != -1) {
        // 找到了换行符，打印出前面的数据
        QByteArray packet = recBuf.left(pos);
        qDebug() << "Received packet:" << packet;

        // 移除已处理的数据
        recBuf.remove(0, pos + 1); // +1 是为了移除换行符本身
       str_rev =processData(packet);
    qDebug()<< str_rev;
    // 解析数据
          double value = str_rev;
          currentData.append(value); // 存储到当前轮次的数据中


          if (currentData.size() == byteArrayList.size()) {
               // 提交任务到线程池（而非直接插入数据库）
               QThreadPool::globalInstance()->start(new DatabaseInsertTask(currentData));
               currentData.clear();
           }





    switch (cs) {
    case 0:
        emit mySignal(str_rev); // 发射信号并传递参数
        break;
    case 1:
        emit mySignal2(str_rev); // 发射信号并传递参数
        break;
    case 2:
        emit mySignal3(str_rev); // 发射信号并传递参数
        break;
    case 3:
        emit mySignal4(str_rev); // 发射信号并传递参数
        break;
    case 4:
        emit mySignal5(str_rev); // 发射信号并传递参数
        break;
    case 5:
        emit mySignal6(str_rev); // 发射信号并传递参数
        break;
    case 6:
        emit mySignal7(str_rev); // 发射信号并传递参数
        break;
    case 7:
        emit mySignal8(str_rev); // 发射信号并传递参数
        break;
    case 8:
        emit mySignal9(str_rev); // 发射信号并传递参数
        break;
    case 9:
        emit mySignal10(str_rev); // 发射信号并传递参数
        break;
        // 您可以根据需要添加更多的 case
    default:
//        array = "101r_pmt_count\r";
        break;
    }

    // 修改 cs 时加锁
    {
        QMutexLocker locker(&csMutex);
        cs = (cs + 1) % 10;
    }
    }
//    isSignalConnected = true;


}

/**
 * @brief 数据解析处理
 * 将接收到的字符串数据转换为数值
 * @param data 接收到的原始数据
 * @return 解析后的数值
 */
double serialPort::processData(const QByteArray &data)
{
    // 假设data是已经组合好的数据，例如"s_nox_k 2.000"或"r_no2_k -1.0 ok"
    QString strData = QString::fromUtf8(data); // 将QByteArray转换为QString

    // 移除可能的后缀，如" ok"
//    strData.remove(QRegExp("\\s+ppb$")); // 使用正则表达式移除以空格开头并后跟"ok"的后缀
    strData.remove(QRegExp("\\s+(ok|off/on|KPa|ppb|mg/m3 |ug/m3 |ppm)$")); // 使用正则表达式移除以空格开头并后跟"ok"或"ppb"的后缀

    // 使用空格分割字符串，假设数据总是以空格分隔
    QStringList parts = strData.split(" ", QString::SkipEmptyParts);

    // 检查我们是否至少有两个部分（即标签和值）
    if (parts.size() >= 2) {
        // 提取第二部分作为值
        QString value = parts.at(1);

        // 去除值中可能存在的非数字字符（这里假设值总是以数字开头，可能包含小数点）
        value.remove(QRegExp("[^\\d.-]")); // 保留数字、小数点和可能的负号

        // 尝试将值转换为浮点数
        bool ok;
        double numericValue = value.toDouble(&ok);
        if (ok) {
            qDebug() << "Extracted numeric value:" << numericValue;
            return numericValue; // 返回处理后的数值作为字符串
        } else {
            qDebug() << "Failed to convert value to numeric:" << value;
        }
    } else {
        qDebug() << "Data format is not as expected:" << strData;
    }

    // 如果没有提取到有效的值，则返回空字符串或其他默认值
    return 0.0 ;
}




